// SpontLight 点光
// 照射所有方向的光源
import { 
  AmbientLight, 
  AxesHelper, 
  BoxGeometry, 
  CameraHelper, 
  Color, 
  Mesh, MeshPhongMaterial, MeshLambertMaterial, 
  Object3D, 
  PerspectiveCamera, PlaneGeometry, Scene, 
  SphereGeometry, 
  SpotLight, 
  SpotLightHelper, 
  Vector3, 
  WebGLRenderer 
} from "three"
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls'
import Stats from 'stats.js'
import dat from 'dat.gui'

const scene = new Scene()
const camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000)
const renderer = new WebGLRenderer()
renderer.setClearColor(0x000000)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true

document.body.appendChild(renderer.domElement)

// 创建一个平面
const planeGeometry = new PlaneGeometry(60, 40, 1, 1)
const planeMaterial = new MeshPhongMaterial({ color: 0xffffff })
const plane = new Mesh(planeGeometry, planeMaterial)
// 接收阴影
plane.receiveShadow = true

plane.rotation.x = -0.5 * Math.PI
plane.position.set(0, 0, 0)
scene.add(plane)

const geometry = new BoxGeometry(4, 4, 4)
const material = new MeshLambertMaterial({ color: 0xff0000 })
const cube = new Mesh(geometry, material)

cube.position.set(-8, 2, 0)
// 正方体投射阴影
cube.castShadow = true

scene.add( cube )

// 添加坐标系，方便理解
const axes = new AxesHelper(15)
scene.add(axes)

// 设置相机位置
camera.position.set(-30, 40, 30)
camera.lookAt(new Vector3(5, 0, 0))

// 添加环境光
let ambientColor = '#1c1c1c'
const ambientLight = new AmbientLight(ambientColor)
scene.add(ambientLight)

// 添加聚光灯
let spotLight0 = new SpotLight(0xcccccc)
spotLight0.position.set(-40, 30, -10)
spotLight0.lookAt(plane.position)
scene.add(spotLight0)
let debugCamera0 = new CameraHelper(spotLight0.shadow.camera)
// scene.add(debugCamera0)

// 添加一个目标与光源
let target = new Object3D()
target.position.copy(new Vector3(5, 0,0))

// 生成另一个聚光灯
let spotLight = new SpotLight('#ffffff')
spotLight.position.set(-40, 60, -10)
spotLight.castShadow = true
spotLight.shadow.camera.near = 1
spotLight.shadow.camera.far = 100
spotLight.target = plane
spotLight.distance = 0
spotLight.angle = 0.4
spotLight.shadow.camera.fov = 120
scene.add(spotLight)

let pp = new SpotLightHelper(spotLight)
scene.add(pp)

let debugCamera = new CameraHelper(spotLight.shadow.camera)
// scene.add(debugCamera)

// 生成一个球
let sphereLight = new SphereGeometry(0.2)
let sphereLightMaterial = new MeshLambertMaterial({color: 0xac6c25})
let sphereLightMesh = new Mesh(sphereLight, sphereLightMaterial)
sphereLightMesh.position.set(3, 20, 3)
sphereLightMesh.castShadow = true
scene.add(sphereLightMesh)

// 添加 FPS 监控
let stats: Stats = new Stats()
stats.showPanel(0)
document.body.appendChild(stats.dom)

const controls = setupControls()
// 引入轨迹球控制器，添加鼠标控制功能
const trackballControls = new TrackballControls(camera, renderer.domElement)

let step = 0
let invert = 1
let phase = 0

function render() {
  trackballControls.update()
  stats.update()

  pp.update()
  debugCamera.update()

  if (!controls.stopMovingLight) {
    if (phase > 2 * Math.PI) {
      invert = invert * -1
      phase -= 2 * Math.PI
    } else {
      phase += controls.rotationSpeed
    }

    sphereLightMesh.position.z = +(7 * Math.sin(phase))
    sphereLightMesh.position.x = +(14 * Math.cos(phase))
    sphereLightMesh.position.y = 15

    if (invert < 0) {
      let pivot = 14
      sphereLightMesh.position.x = (invert * (sphereLightMesh.position.x - pivot)) + pivot
    }

    spotLight.position.copy(sphereLightMesh.position)
  }

  requestAnimationFrame(render)
  renderer.render(scene, camera)
}
render()

function setupControls() {
  // 控制器
  let controls = new function () {
    this.rotationSpeed = 0.03
    this.bouncingSpeed = 0.03
    this.ambientColor = ambientLight.color.getStyle()
    this.pointColor = spotLight.color.getStyle()
    this.distance = 0
    this.intensity = 1
    this.angle = 0.1
    this.shadowDebug = false
    this.castShadow = true
    this.target = 'Plane'
    this.stopMovingLight = false
    this.penumbra = 0
  }

  const gui = new dat.GUI()
  // 颜色选择功能
  gui.addColor(controls, 'ambientColor')
    .onChange(e => {
      ambientLight.color = new Color(e)
    })

  gui.addColor(controls, 'pointColor').onChange(e => {
    spotLight.color = new Color(e)
  })
  // 角度
  gui.add(controls, 'angle', 0, Math.PI * 2).onChange(e => {
    spotLight.angle = e
  })
  // 强度
  gui.add(controls, 'intensity', 0, 5).onChange(e => {
    spotLight.intensity = e
  })
  // 边缘柔和
  gui.add(controls, 'penumbra', 0, 1).onChange(e => {
    spotLight.penumbra = e
  })

  gui.add(controls, 'distance', 0, 200).onChange(e => {
    spotLight.distance = e
  })

  gui.add(controls, 'shadowDebug').onChange(e => {
    if (e) {
      scene.add(debugCamera)
    } else {
      scene.remove(debugCamera)
    }
  })

  gui.add(controls, 'castShadow').onChange(e => {
    spotLight.castShadow = e
  })

  gui.add(controls, 'target', ['Plane', 'Cube']).onChange(e => {
    switch (e) {
      case 'Plane':
        spotLight.target = plane
        break
      case 'Cube':
        spotLight.target = cube
        break
    }
  })

  gui.add(controls, 'stopMovingLight').onChange(e => {
    controls.stopMovingLight = e
  })

  return controls
}